﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;

public class Steve:ViewableObject
{
    public bool hitGround
    {
        get { return justStepped; }
    }

    protected static Texture2D shadow = Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.ENVIRONMENT).get("shadow").spriteSheet;
    protected Rectangle shadowDest = new Rectangle();
    protected bool justStepped = false;
    // TODO: Make these bounds get set based on the size of the actual level
    protected int worldMinX = 0;
    protected int worldMaxX = 10500;
    protected int worldFloorY = 5000;
    protected SpriteEffects effects = SpriteEffects.None;
    protected new Steve.Type thisType;
    protected double stepDelay = 5000;
    protected double sinceStep = 0;
    protected double liftDelay = 1000;
    protected double liftTimer = -1;
    protected int stepDistance = 700;
    // We can be +/- this y-distance from the floor to still count as onTheFloor for movement checks
    protected int floorLeeway = 100;
    protected Vector3 stepSpeed = new Vector3(0, 10, 0);
    protected Vector3 currVelocity = new Vector3();
    protected Direction currDir = Direction.Left;
    protected Animation foot = new Animation("stevefoot", 0, true,
            new ArrayList{
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.STEVE).get("steve"),
                    new Rectangle(0,0,1107,2046),
                    0,
                    new Vector2(660,1960)),
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.STEVE).get("steve"),
                    new Rectangle(0,0,1107,2046),
                    0,
                    new Vector2(447,1960))});
    public enum Direction
    {
        Left,Right
    }

    public Steve(Steve.Type type, Vector3 placement)
        : base(type, placement)
    {
        thisType = type;
        currentAnimation = foot;
        currVelocity = stepSpeed;
    }

    /**
     * Return the direction we're facing. Used by the HUD to draw our graphic flipped if needed.
     */
    public Direction getDirection()
    {
        return currDir;
    }

    public void changeDirection(Steve.Direction direction)
    {
        currDir = direction;
        switch (direction)
        {
            case Direction.Right:
                effects = SpriteEffects.FlipHorizontally;
                foot.setCurrentFrame(1);
                break;
            case Direction.Left:
                effects = SpriteEffects.None;
                foot.setCurrentFrame(0);
                break;
        }
    }

    public override void update(double elapsed)
    {
        updateMovement(elapsed);
        currentAnimation.update(elapsed);
        screenCoord = Camera.worldToScreen(origin);
        updateShadow();
        ((LevelBase)Game.getGame().getEnvStack().currentEnv()).getHUD().setStevePosition(origin, this);
    }

    protected void updateShadow()
    {
        //find the closest walkable object
        //find x/y placement of the shadow
        //make rect size proportional to height
        float shadowY = float.MaxValue;
        List<CollidableObject> floorCheck = LevelBase.collidesWith(
            new Vector3(min.X, min.Y + thisType.collisionBoxDimentions.Y, min.Z),
            new Vector3(max.X, max.Y + 1000, max.Z),
            this);
        foreach (CollidableObject c in floorCheck)
        {
            if (c is ViewableObject && ((ViewableObject)c).walkable && shadowY > c.collisionMin.Y)
            {
                shadowY = c.collisionMin.Y;
            }
        }
        float distanceFactor = (shadowY - origin.Y) / 1000;
        Vector3 backLeft = new Vector3(origin.X - thisType.collisionBoxOriginOffset.X, shadowY, origin.Z + thisType.collisionBoxDimentions.Z);
        Vector3 frontRight = new Vector3(backLeft.X + thisType.collisionBoxDimentions.X, shadowY, origin.Z);
        Vector3 screenBackLeft = Camera.worldToScreen(backLeft);
        Vector3 screenFrontRight = Camera.worldToScreen(frontRight);
        float widthDiff = (screenFrontRight.X - screenBackLeft.X) * distanceFactor;
        float heightDiff = (screenFrontRight.Y - screenBackLeft.Y) * distanceFactor;
        shadowDest = new Rectangle(
            (int)(screenBackLeft.X + (widthDiff / 2)),
            (int)(screenBackLeft.Y + (heightDiff / 2)),
            (int)(screenFrontRight.X - screenBackLeft.X - widthDiff),
            (int)(screenFrontRight.Y - screenBackLeft.Y - heightDiff));
    }

    private void updateMovement(double elapsed)
    {
        justStepped = false;
        sinceStep += elapsed;
        if(sinceStep > stepDelay)
        {
            sinceStep = 0;
            switch (currDir)
            {
                case Direction.Left:
                    if (Math.Abs(position.Y - worldFloorY) >= floorLeeway)
                    {
                        // Only move if we're in the air currently
                        move(new Vector3(-stepDistance, 0, 0));
                    }
                    if (origin.X < worldMinX)
                    {
                        changeDirection(Direction.Right);
                    }
                    break;
                case Direction.Right:
                    if (Math.Abs(position.Y - worldFloorY) >= floorLeeway)
                    {
                        // Only move if we're in the air currently
                        move(new Vector3(stepDistance, 0, 0));
                    }
                    if (origin.X > worldMaxX)
                    {
                        changeDirection(Direction.Left);
                    }
                    break;
            }
            currVelocity = stepSpeed;
        }
        if (liftTimer > liftDelay)
        {
            currVelocity = -stepSpeed;
            liftTimer = -1;
        }
        else if (liftTimer >= 0)
        {
            liftTimer += elapsed;
        }
        move(currVelocity);
        if (liftTimer == -1)
        {
            //this makes sure he doesnt stomp thru the floor
            //when not on screen
            if(origin.Y > worldFloorY)
            {
                currVelocity = new Vector3(0, 0, 0);
                move(new Vector3(0, worldFloorY - origin.Y, 0));
                liftTimer = 0;
                justStepped = true;
            }
            /*
            List<CollidableObject> floorCheck = LevelBase.collidesWith(this);
            foreach (CollidableObject co in floorCheck)
            {
                if (co is ViewableObject && ((ViewableObject)co).walkable)
                {
                    currVelocity = new Vector3(0, 0, 0);
                    move(new Vector3(0,co.collisionMin.Y - origin.Y,0));
                    liftTimer = 0;
                }
            }
             * */
        }
        currentAnimation.setEffects(effects);
    }

    protected void move(Vector3 amount)
    {
        origin += amount;
        min += amount;
        max += amount;
    }

    public override void draw()
    {
        base.draw();
        Game.getGame().getSpriteBatch().Draw(shadow, shadowDest, null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, screenCoord.Z + (thisType.depthOffset));
    }

    private void loadSounds(){

    }

    public new class Type : ViewableObject.Type
    {
        public static readonly Steve.Type Steve = new Type(
            ObjectFactory.TypeEnum.Steve,
            new Vector3(155, 900, 0),
            new Vector3(310, 900, 200),
            new ArrayList{
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.STEVE).get("steve"),
                    new Rectangle(0,0,1107,2046),
                    0,
                    new Vector2(660,1960)),
                new Frame(
                    Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.STEVE).get("steve"),
                    new Rectangle(0,0,1107,2046),
                    0,
                    new Vector2(447,1960))});


        /**
         * Private constructor.
         * This private constructor ensures that only 
         * the pre-specified Type objects can be used 
         * and compared against.
         * @param origin
         * @param collision
         * @param basic
         * @param destruction
         */
        protected Type(ObjectFactory.TypeEnum type, Vector3 origin, Vector3 collision,
            ArrayList stasisFrames)
            : base(type, origin, collision, stasisFrames)
        {
            sDepth = 0;
        }

        public static new Steve.Type getType(ObjectFactory.TypeEnum type)
        {
            switch (type)
            {
                case ObjectFactory.TypeEnum.Steve: return Steve;
            }
            return null;
        }
    }
}
